package com.situ.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.situ.config.JwtProperties;
import com.situ.context.BaseContext;
import com.situ.domain.dto.*;
import com.situ.domain.entity.TUser;
import com.situ.domain.vo.BasePageQueryVO;
import com.situ.domain.vo.BussinessReportVO;
import com.situ.domain.vo.UserReportVO;
import com.situ.ex.AttackException;
import com.situ.ex.UserException;
import com.situ.ex.UserNotFoundException;
import com.situ.ex.UsernameDuplicatedException;
import com.situ.mapper.TUserMapper;
import com.situ.service.UserService;
import com.situ.utils.JwtUtil;
import com.situ.utils.SaltCodeUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements UserService {


    private final JwtProperties jwtProperties;

    /**
     * 用户注册
     *
     * @param registerDTO
     */
    @Override
    public void register(RegisterDTO registerDTO) {
        TUser one = this.lambdaQuery()
                .eq(TUser::getUsername, registerDTO.getUsername())
                .one();
        if (one != null) {
            log.info("用户名已存在");
            throw new UsernameDuplicatedException();
        }
        if (!registerDTO.getPassword().equals(registerDTO.getPassword2())) {
            throw new RuntimeException("密码不一致");
        }
        String saltCode = SaltCodeUtils.generateSaltCode(8);
        this.save(TUser
                .builder()
                .username(registerDTO.getUsername())
                .password(getMdtPassword(registerDTO.getPassword(),
                        registerDTO.getUsername(), saltCode).toUpperCase())
                .isDelete(0)
                .salt(saltCode)
                .avatar("http://124.156.204.126:9000/hello-minio/defaultImage..jpg")
                .createdTime(new Date())
                .createdUser(registerDTO.getUsername())
                .modifiedTime(new Date())
                .build());
    }

    /**
     * 用户登录
     *
     * @param logInDTO
     * @return
     */
    @Override
    public String userLogin(LogInDTO logInDTO) {
        TUser user = this.lambdaQuery()
                .eq(TUser::getUsername, logInDTO.getUsername())
                .one();
        if (user == null) {
            throw new UserNotFoundException("用户不存在");
        }
        if (user.getIsDelete() == 1) {
            throw new UserNotFoundException("用户已注销");
        }
        boolean isSuccess = user.getPassword().equals(
                getMdtPassword(
                        logInDTO.getPassword(),
                        logInDTO.getUsername(),
                        user.getSalt()
                ).toUpperCase()
        );
        if (!isSuccess) {
            throw new UserNotFoundException("用户名或密码错误");
        }
        Map<String, Object> claim = new HashMap<>();
        claim.put("userId", user.getUid());
        String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claim);
        return token;
    }

    /**
     * 用户修改密码
     *
     * @param userPasswordDTO
     */
    @Override
    public void changeUserPassword(ChangeUserPasswordDTO userPasswordDTO) {
        TUser user = this.lambdaQuery()
                .eq(TUser::getUid, userPasswordDTO.getUid())
                .eq(TUser::getIsDelete, 0)
                .one();
        if (user == null) {
            throw new UserException("用户不存在");
        }
        if (!user.getUid().equals(BaseContext.getCurrentId())) {
            throw new AttackException("司马盗token玩意");
        }
        // 校验原密码是否正确
        if (!user.getPassword().equals(
                getMdtPassword(
                        userPasswordDTO.getOldPassword(),
                        user.getUsername(),
                        user.getSalt()
                ).toUpperCase()
        )) {
            throw new UserException("原密码错误");
        }
        // 修改密码
        this.lambdaUpdate()
                .eq(TUser::getUid, user.getUid())
                .set(TUser::getPassword,
                        getMdtPassword(
                                userPasswordDTO.getPassword(),
                                user.getUsername(),
                                user.getSalt()
                        ).toUpperCase())
                .set(TUser::getModifiedTime, new Date())
                .set(TUser::getModifiedUser, user.getUsername())
                .update();
    }

    /**
     * 获取用户信息
     * @return
     */
    @Override
    public UserInfoDTO getUserInfo() {
        TUser user = this.lambdaQuery()
                .eq(TUser::getUid, BaseContext.getCurrentId())
                .eq(TUser::getIsDelete, 0)
                .one();
        if (user == null) {
            throw new AttackException("司马伪造token");
        }
        return UserInfoDTO
                .builder()
                .username(user.getUsername())
                .phone(user.getPhone())
                .email(user.getEmail())
                .gender(user.getGender())
                .build();
    }

    /**
     * 分页查询用户
     * @param pageQueryDTO
     * @return
     */
    @Override
    public BasePageQueryVO<TUser> searchUsersByPage(UserPageQueryDTO pageQueryDTO) {
        Page page = Page.of(pageQueryDTO.getCurrentPage(), pageQueryDTO.getPageSize());
        this.lambdaQuery()
                .like(StrUtil.isNotBlank(pageQueryDTO.getQueryString()), TUser::getUsername, pageQueryDTO.getQueryString())
                .or()
                .like(StrUtil.isNotBlank(pageQueryDTO.getQueryString()),TUser::getEmail,pageQueryDTO.getQueryString())
                .page(page);
        return new BasePageQueryVO(page.getCurrent(),
                page.getSize(),
                page.getTotal(),
                page.getPages(),
                page.getRecords());
    }


    /**
     * 修改用户状态
     * @param uid
     * @param isDelete
     */
    @Override
    public void changeStatus(Integer uid, Integer isDelete) {
        this.lambdaUpdate()
                .eq(TUser::getUid, uid)
                .set(TUser::getIsDelete, isDelete)
                .set(TUser::getModifiedTime, new Date())
                .set(TUser::getModifiedUser, BaseContext.getCurrentId().toString())
                .update();
    }


    /**
     * 获取用户报表
     * @return
     */
    @Override
    public UserReportVO geUserReport() {
        LocalDate localDate = LocalDate.now();
        List<String> months = new ArrayList<>();
        List<Long> number = new ArrayList<>();
        for (int i = 11; i >= 0; i -- ) {
            LocalDate localDate1 = localDate.plusMonths(-i);
            months.add(DateUtil.format(LocalDateTime.of(localDate1, LocalTime.MAX), "yyyy-MM"));
            LocalDateTime begin = LocalDateTime.of(localDate1.with(TemporalAdjusters.firstDayOfMonth()),LocalTime.MIN);
            LocalDateTime end = LocalDateTime.of(localDate1.with(TemporalAdjusters.lastDayOfMonth()),LocalTime.MAX);
            // 查数据库
            Long count = this.lambdaQuery()
                    .ge(TUser::getCreatedTime, begin)
                    .le(TUser::getCreatedTime, end)
                    .eq(TUser::getIsDelete, 0)
                    .count();
            number.add(count);
        }
        return UserReportVO
                .builder()
                .months(months)
                .number(number)
                .build();
    }

    /**
     * 获取业务报表
     * @return
     */
    @Override
    public BussinessReportVO.UserReport getBussinessUserReport() {
        Long todayNewMember = this.lambdaQuery()
                .ge(TUser::getCreatedTime, DateUtil.beginOfDay(new Date()))
                .eq(TUser::getIsDelete, 0)
                .count();

        Long thisWeekNewMember = this.lambdaQuery()
                .ge(TUser::getCreatedTime, DateUtil.beginOfDay(DateUtil.beginOfWeek(new Date())))
                .eq(TUser::getIsDelete, 0)
                .count();

        Long thisMonthNewMember = this.lambdaQuery()
                .ge(TUser::getCreatedTime, DateUtil.beginOfDay(DateUtil.beginOfMonth(new Date())))
                .eq(TUser::getIsDelete, 0)
                .count();

        return BussinessReportVO.UserReport
                .builder()
                .totalMember(this.lambdaQuery().count())
                .todayNewMember(todayNewMember)
                .thisWeekNewMember(thisWeekNewMember)
                .thisMonthNewMember(thisMonthNewMember)
                .build();
    }

    /**
     * 获取MD5密码
     *
     * @param password
     * @param username
     * @param salt     盐值
     * @return
     */
    private String getMdtPassword(String password,
                                  String username,
                                  String salt) {
        for (int i = 0; i < 3; i++) {
            String newPs = salt + password + username;
            password = DigestUtil.md5Hex(newPs);
        }
        return password;
    }
}
